import math

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.patches as patches

import utils.build
import utils.road
import utils.water
import utils.tree
import utils.mountain


def normalize_coordinates(points):
    """
    将坐标点标准化到0-100范围

    参数:
        points: 二维坐标点列表，格式为[(x1, y1), (x2, y2), ...]

    返回:
        normalized_points: 标准化后的坐标点数组
    """
    points_array = np.array(points)
    x_min, y_min = np.min(points_array, axis=0)
    x_max, y_max = np.max(points_array, axis=0)

    # 避免除零错误
    if x_max - x_min == 0:
        x_normalized = np.zeros_like(points_array[:, 0])
    else:
        x_normalized = (points_array[:, 0] - x_min) / (x_max - x_min) * 10

    if y_max - y_min == 0:
        y_normalized = np.zeros_like(points_array[:, 1])
    else:
        y_normalized = (points_array[:, 1] - y_min) / (y_max - y_min) * 10

    return np.column_stack((x_normalized, y_normalized))


def calculate_grid_density(points, grid_size=10, cell_size=1):
    """
    计算点在网格中的分布密度

    参数:
        points: 标准化后的坐标点
        grid_size: 网格大小
        cell_size: 单元格大小

    返回:
        density_grid: 密度网格数组
    """
    # 初始化密度网格
    num_cells = grid_size // cell_size
    density_grid = np.zeros((num_cells, num_cells))

    # 计算每个点所在的网格位置
    for point in points:
        x, y = point
        if 0 <= x < 100 and 0 <= y < 100:
            i = int(x // cell_size)
            j = int(y // cell_size)
            if i < num_cells and j < num_cells:
                density_grid[i, j] += 1

    return density_grid


def visualize_density(density_grid):
    plt.rcParams['font.family'] = ['SimHei']
    """
    可视化网格密度

    参数:
        density_grid: 密度网格数组
    """
    fig, ax = plt.subplots(figsize=(10, 8))

    # 创建自定义颜色映射
    colors = ['#fdeff4', '#f9b8d4', '#f48fb1', '#e91e63', '#d81b60', '#c2185b', '#ad1457', '#880e4f', '#4a148c']
    cmap = LinearSegmentedColormap.from_list('custom_blue', colors, N=256)

    # 显示密度网格
    im = ax.imshow(density_grid.T, origin='lower', cmap=cmap,
                   extent=[0, 10, 0, 10], aspect='equal')

    # 添加颜色条
    cbar = plt.colorbar(im, ax=ax, shrink=0.8)
    cbar.set_label('点密度', fontsize=12)

    # 设置标题和标签
    ax.set_title('不同园林的相似度分析', fontsize=14)
    ax.set_xlabel('X 坐标', fontsize=12)
    ax.set_ylabel('Y 坐标', fontsize=12)

    # 添加网格线
    ax.grid(True, color='gray', linestyle='--', linewidth=0.5, alpha=0.7)

    plt.tight_layout()
    plt.show()


def print_density_statistics(density_grid):
    """
    打印密度统计信息

    参数:
        density_grid: 密度网格数组
    """
    total_points = np.sum(density_grid)
    max_density = np.max(density_grid)
    avg_density = np.mean(density_grid[density_grid > 0])
    empty_cells = np.sum(density_grid == 0)
    total_cells = density_grid.size

    print("网格密度统计信息:")
    print(f"总点数: {int(total_points)}")
    print(f"最高密度: {max_density:.2f} 点/网格")
    print(f"平均密度: {avg_density:.2f} 点/网格 (非空网格)")
    print(f"空网格数: {empty_cells} ({empty_cells / total_cells * 100:.2f}%)")
    print(f"非空网格数: {total_cells - empty_cells} ({(total_cells - empty_cells) / total_cells * 100:.2f}%)")


def matrix_similarity_cosine(A, B):
    """
    将矩阵展平为向量后计算余弦相似度。
    更关注两个矩阵模式的相似性，而对元素的绝对大小不敏感。
    """
    # 将矩阵展平为一维向量
    vA = A.flatten()
    vB = B.flatten()

    # 计算点积
    dot_product = np.dot(vA, vB)
    # 计算模长
    norm_A = np.linalg.norm(vA)
    norm_B = np.linalg.norm(vB)

    if norm_A == 0 or norm_B == 0:
        return 0.0  # 如果存在零向量，则相似度为0

    cosine_sim = dot_product / (norm_A * norm_B)
    # 余弦相似度范围通常在[-1,1]，但我们通常需要非负的，所以取绝对值或将其映射到[0,1]
    # 根据你的需求，如果矩阵元素可能有正有负，且方向相反你认为完全不同，可以直接取(cosine_sim + 1) / 2
    # 如果只关心模式相似不关心正负方向，可以取绝对值
    return abs(cosine_sim)
    # 或者 return (cosine_sim + 1) / 2


def progressBar(index, all=500):
    # 显示进度条
    per = ((index / all) * 100) + 1
    if per > 100: per = 100
    print(
        f'\r【系统提示】 进度分析: [{"█" * math.floor(60 * per / 100)}{" " * math.floor(60 * (100 - per) / 100)}] {per:.0f}%',
        end='', flush=True)


# 示例使用
if __name__ == "__main__":

    grid = np.zeros((10, 10))

    # 建筑
    for i in range(1, 11):
        points_A = utils.build.getBuildPoints(i)
        for j in range(1, 11):
            points_B = utils.build.getBuildPoints(j)
            progressBar(((i * 10)+j), 500)
            # 1. 标准化坐标点（虽然示例数据已在0-100范围，但演示标准化过程）
            normalized_points_A = normalize_coordinates(np.vstack(points_A))
            normalized_points_B = normalize_coordinates(np.vstack(points_B))

            # 2. 计算网格密度
            density_grid_A = calculate_grid_density(normalized_points_A)
            density_grid_B = calculate_grid_density(normalized_points_B)

            sim_cosine = matrix_similarity_cosine(density_grid_A, density_grid_B)
            grid[i - 1, j - 1] = grid[i - 1, j - 1] + (sim_cosine * 0.2)
    # 水体
    for i in range(1, 11):
        points_A = utils.water.getWaterPoints(i)
        for j in range(1, 11):
            points_B = utils.water.getWaterPoints(j)
            progressBar(((i * 10)+j) + 100, 500)
            # 1. 标准化坐标点（虽然示例数据已在0-100范围，但演示标准化过程）
            normalized_points_A = normalize_coordinates(np.vstack(points_A))
            normalized_points_B = normalize_coordinates(np.vstack(points_B))

            # 2. 计算网格密度
            density_grid_A = calculate_grid_density(normalized_points_A)
            density_grid_B = calculate_grid_density(normalized_points_B)

            sim_cosine = matrix_similarity_cosine(density_grid_A, density_grid_B)
            grid[i - 1, j - 1] = grid[i - 1, j - 1] + (sim_cosine * 0.2)
    # 树木
    for i in range(1, 11):
        points_A = utils.tree.getTreePoints(i)
        for j in range(1, 11):
            points_B = utils.tree.getTreePoints(j)
            progressBar(((i * 10)+j) + 200, 500)
            # 1. 标准化坐标点（虽然示例数据已在0-100范围，但演示标准化过程）
            normalized_points_A = normalize_coordinates(np.vstack(points_A))
            normalized_points_B = normalize_coordinates(np.vstack(points_B))

            # 2. 计算网格密度
            density_grid_A = calculate_grid_density(normalized_points_A)
            density_grid_B = calculate_grid_density(normalized_points_B)

            sim_cosine = matrix_similarity_cosine(density_grid_A, density_grid_B)
            grid[i - 1, j - 1] = grid[i - 1, j - 1] + (sim_cosine * 0.2)
    # 假山
    for i in range(1, 11):
        points_A = utils.mountain.getMountainPoints(i)
        for j in range(1, 11):
            points_B = utils.mountain.getMountainPoints(j)
            progressBar(((i * 10)+j) + 300, 500)
            # 1. 标准化坐标点（虽然示例数据已在0-100范围，但演示标准化过程）
            normalized_points_A = normalize_coordinates(np.vstack(points_A))
            normalized_points_B = normalize_coordinates(np.vstack(points_B))

            # 2. 计算网格密度
            density_grid_A = calculate_grid_density(normalized_points_A)
            density_grid_B = calculate_grid_density(normalized_points_B)

            sim_cosine = matrix_similarity_cosine(density_grid_A, density_grid_B)
            grid[i - 1, j - 1] = grid[i - 1, j - 1] + (sim_cosine * 0.2)
    # 道路
    for i in range(1, 11):
        points_A = utils.road.getRoadPoints(i)
        for j in range(1, 11):
            points_B = utils.road.getRoadPoints(j)
            progressBar(((i * 10)+j) + 400, 500)
            # 1. 标准化坐标点（虽然示例数据已在0-100范围，但演示标准化过程）
            normalized_points_A = normalize_coordinates(np.vstack(points_A))
            normalized_points_B = normalize_coordinates(np.vstack(points_B))

            # 2. 计算网格密度
            density_grid_A = calculate_grid_density(normalized_points_A)
            density_grid_B = calculate_grid_density(normalized_points_B)

            sim_cosine = matrix_similarity_cosine(density_grid_A, density_grid_B)
            grid[i - 1, j - 1] = grid[i - 1, j - 1] + (sim_cosine * 0.2)

    # 4. 可视化
    print(grid)
    visualize_density(grid)
